package com.config.ser.controller;

import com.config.ser.request.AddConfigRequest;
import com.config.ser.service.CloudConfigService;
import com.config.ser.vo.CheckMD5VO;
import com.config.ser.vo.CloudConfigVO;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.List;

@RestController
public class CloudConfigController {

    @Autowired(required = false)
    private CloudConfigService cloudConfigService;

    //guava中的Multimap，多值map,对map的增强，一个key可以保持多个value
    private Multimap<String, DeferredResult<CheckMD5VO>> watchRequests = Multimaps.synchronizedSetMultimap(HashMultimap.create());


    /**
     * 长轮询校验MD5是否相同
     * @param id  微服务在配置中心对应的id
     * @deprecated 这里使用长轮询，所以无需调用真正的查询，结果依靠修改接口传进来即可
     *             当配置文件修改成功是，则会立刻返回结果
     *
     * @return
     */
    @GetMapping("check_md5")
    public DeferredResult<CheckMD5VO> checkMD5(@RequestParam(value = "id") String id, HttpServletResponse response){
        DeferredResult<CheckMD5VO> deferredResult = new DeferredResult<>();
        //当deferredResult完成时（不论是超时还是异常还是正常完成），移除watchRequests中相应的watch key
        deferredResult.onCompletion(new Runnable() {
            @Override
            public void run() {
                watchRequests.remove(id, deferredResult);
            }
        });
        watchRequests.put(id, deferredResult);
        return deferredResult;
    }

    /**
     * 添加配置文件
     * @param addConfigRequest
     * @return
     */
    @PostMapping("add_config")
    public boolean addConfig(@RequestBody AddConfigRequest addConfigRequest){

        return cloudConfigService.addConfig(addConfigRequest);
    }

    /**
     * 修改配置文件(更新完毕需要给deferredResult赋值，触发长轮询)
     * @param addConfigRequest
     * @return
     */
    @PostMapping("upd_config")
    public boolean updConfig(@RequestBody AddConfigRequest addConfigRequest) throws FileNotFoundException {
        CheckMD5VO checkMD5VO = cloudConfigService.updConfig(addConfigRequest);
        if(checkMD5VO == null){
            return false;
        }

        // 配置文件真的有变动的时候再进行长轮询，如果配置文件没变动或者只是原样提交不需要改deferredResult
        if(!checkMD5VO.getEq()) {
            // 启用长轮询，把修改后新的配置传给查询接口返回的map，真正的查询是在修改中调用的，把结果传给map
            if (watchRequests.containsKey(addConfigRequest.getId().toString())) {
                Collection<DeferredResult<CheckMD5VO>> deferredResults = watchRequests.get(addConfigRequest.getId().toString());
                //通知所有watch这个namespace变更的长轮训配置变更结果
                for (DeferredResult<CheckMD5VO> deferredResult : deferredResults) {
                    deferredResult.setResult(checkMD5VO);
                }
            }
        }
        return true;
    }

    /**
     * 查询配置文件配置文件
     * @param serverName  服务名
     * @return
     */
    @GetMapping("select_config")
    public CloudConfigVO selectConfig(@RequestParam(value = "serverName") String serverName){
        CloudConfigVO cloudConfigVO = cloudConfigService.selectConfig(serverName);
        return cloudConfigVO;
    }

    /**
     * 删除配置文件
     * @param serverName  服务名
     * @return
     */
    @PostMapping("del_config")
    public boolean delConfig(@RequestParam(value = "serverName") String serverName){
        return cloudConfigService.delConfig(serverName);
    }

    /**
     * 查询所有配置文件配置文件
     * @return
     */
    @GetMapping("select_all_config")
    public List<CloudConfigVO> selectAllConfig(){

        return cloudConfigService.selectAllConfig();
    }

    /**
     * 客户端初次启动来拉取配置文件
     * @param serverName 服务名
     * @return
     */
    @GetMapping("client_first_start_pull_config")
    public CheckMD5VO clientFirstStartPullConfig(@RequestParam("serverName") String serverName){
        CheckMD5VO checkMD5VO = cloudConfigService.clientFirstStartPullConfig(serverName);
        return checkMD5VO;
    }
}
